#! /usr/bin/env python
# -*- coding: UTF-8 -*-
# animationx.py
# this program can let user manager their animations that
# they are watching
#
# Writen by RAh-XePhON
# Jimmy09140456@gmail.com
# Http://ToKyo-JuPiTer.Blogspot.com
# 
# Homepage:    http://code.google.com/p/animachk/
# Ver 0.0.1 @ 080224

import os
from xml.dom import minidom, Node
import pdb

class Parser():
    """the parser which can handle the XML file for sever operations
        Because the XML file is using utf-8 encoding
        So, while calling function print
        NEED to encoding the string by utf-8 otherwise whill have an ERROR
        """

    def __init__(self):
        """class initial"""
        # the XML which saved the animations' data
        self.xml_file = self.localPath() + os.sep + ".datax"
        # Create the minidom doc
        self.doc = minidom.parse(self.xml_file)
        
        # get the numbers, which is a numbers that how many animations' data in XML
        # the numbers is inside a tag which name is "numbers"
        # use getElementsByTagName to find the element, it should find only one element
        # after found the element, the nodeValue of that element's firstChild is numbers
        if len( self.doc.getElementsByTagName("numbers") ) == 1:
            # make sure that only find one element which tagName is numbers
            numbers_node = self.doc.getElementsByTagName("numbers")[0]
            # Notics that the nodeValue is in Unicode format, need to be converted into int
            self.numbers = int( numbers_node.firstChild.nodeValue )
        
    def localPath(self):
        """this function will return the workpath of this program"""
        # initial the working path
        work_path = ""
        # __file__ is the locale of this program file,
        # usually is an absolute path, but still use abspath make to ensure the path
        locale_path = os.path.abspath( __file__ )
        # now, the directory name is the working path
        work_path = os.path.dirname( locale_path )
        # return the working paht
        return work_path
    
    def insertAfter(self, new, target):
        """this function can insert an Element after target Element"""
        parent = target.parentNode
        if (parent.lastChild == target):
            parent.appendChild(new)
        else:
            parent.insertBefore(new, target.nextSibling)
    
    def data2xml(self):
        """using for convert the old data file into xml format
            this function is 'invisible' normally
            if you want to convert the old data file into xml format
            just open a python shell in the directory which the file .dafi stand in
            and type:
                import animationx
                animationx.Parser().data2xml()"""
        # open the old data file
        try:
            data_file = open(".dafi")
            # read the data insidt the file
            data_old = data_file.read()
            # because the file is saving into utf-8 formate and default is using ascii to decode
            # So, need to decode by utf-8
            # Since using utf-8 decode, while calling function print to print out somethings
            # NEED to encoding by utf-8, otherwise will have Error
            data = data_old.decode('utf-8')
            data_file.close()
            
        except IOError:
            print "can NOT open .daft file, please check it!!!"
            raise SystemExit
        
        if not len(data):
            # if the file does not contant anything, print out an error message and exit
            print "the file does NOT contant anything!!!"
            raise SystemExit
        
        # here is copy from animachk.py
        # it is how animachk.py read the data from file
        rec = []
        lines = 0
        c = 1
        for anima_record in data.splitlines():
            anima = anima_record.split()
            if anima[0] == 'Q':    c = 0
            if c:
                lines += 1
                try:
                    rec += [ [ anima[0], anima[1], anima[2], anima[3], anima[4] ] ]
                except IndexError:
                    print "the data file is not correct!!!\nPlease check it"
                    raise SystemExit
        # already read all data from file
        # now add them to the XML file
        last_ani = self.searchAni(self.numbers)
        # find the data tag
        if len( self.doc.getElementsByTagName('data') ) == 1:
            # ensure find only one element which tag name is data
            data_node = self.doc.getElementsByTagName('data')[0]
        
        # create every entries for all recodes inside rec
        for i in range(lines):
            # because numbers using for count howmany ani entries in XML file,
            # so need to plus one
            self.numbers += 1
            
            # all element create here, need to be appended an new line text node
            # create the ani element, all data are save inside it
            new_ani = self.doc.createElement("ani")
            new_ani_nl = self.doc.createTextNode('\n' + ' ' * 8)
            # Attribute n is the number of the entrie
            # notice that self.numbers need to be converted into string
            new_ani.setAttribute("n", str( self.numbers ))
            
            # create name element, it save the animation's name
            new_name = self.doc.createElement("name")
            new_name_nl = self.doc.createTextNode('\n' + ' ' * 12)
            # Attribute url is the offical website of the animation
            new_name.setAttribute("url", rec[i][4])
            # set watch attribute, it using for count which episode is watched
            new_name.setAttribute('watch', rec[i][1])
            # set down attribute, it using for count which episode is downloaded
            new_name.setAttribute('down', rec[i][0])
            # get the animation name and save into a text node
            name_text = self.doc.createTextNode( rec[i][3] )
            new_name.appendChild(name_text)
            
            # create time element, it is saved the broadcast time
            new_time = self.doc.createElement("time")
            new_time_nl = self.doc.createTextNode('\n' + ' ' * 12)
            # get the time, also save into a text node
            time_text = self.doc.createTextNode( rec[i][2] )
            new_time.appendChild(time_text)
            
            # create info element, it is saved the information of the animation
            new_info = self.doc.createElement("info")
            info_text = self.doc.createTextNode(u"None")
            new_info_nl = self.doc.createTextNode('\n' + ' ' * 12)
            new_info.appendChild(info_text)
            
            # append all element above to new_ani
            new_ani.appendChild(new_name)
            new_ani.appendChild(new_time)
            new_ani.appendChild(new_info)
            
            # append new_ani to data after last_ani
            self.insertAfter(new_ani, last_ani)
            last_ani = new_ani
            
            # append the new line text node
            data_node.insertBefore(new_ani_nl, new_ani)
            new_ani.insertBefore(new_name_nl, new_name)
            new_ani.insertBefore(new_time_nl, new_time)
            new_ani.insertBefore(new_info_nl, new_info)
            # append the new line aftel info tag, it shoule be before </ani>
            new_ani_nl2 = new_ani_nl.cloneNode('false')
            new_ani.appendChild(new_ani_nl2)
            
            
            
        self.editNumbers()
            

    def editNumbers(self):
        """this function can edit the numbers entrie in XML file"""
        # the method of get the numbers entrie is same as __init__ above
        if len( self.doc.getElementsByTagName("numbers") ) == 1:
            # make sure that only find one element which tagName is numbers
            numbers_node = self.doc.getElementsByTagName("numbers")[0]
            numbers_node.firstChild.nodeValue = self.numbers
        print self.doc.toxml()
        self.saveXML()
        
    def editAttribute(self, n, att, way):
        """just a protype for edit XML file"""
        # find an node which tag is ani
        # initial the act
        act = "000"
        # find the target node, if should be an tag which name is name 
        # all attribute are inside the tag and 
        # normally should find only one element
        node = self.searchAni(n)
        name = node.getElementsByTagName('name')
        if len(name) == 1:
            name = name[0]
        
        # First need to get the original number of the attribute
        act = name.getAttribute(att)
        # because some entries' attrubites is not digitals, so need to check
        if way[0] == '+' and act.isdigit():
            act = int(act)
            # it is the plus action, it shuld make the down attribute or watch attribute add something
            # way is like +1, an plus sign with a number.
            # way[0] shoule be the plus sign and way[1:] should be the number
            t = int( way[1:] )
            act += t
            act = "%03d" % act
        elif way.isdigit():
            # if way is digitals, set the attribute to that number
            act = way
        name.setAttribute(att, act)
        self.saveXML()
        
    def saveXML(self):
        """this function for savine the XML file after edit the data"""
        # get the data which in string format 
        #data = self.doc.toprettyxml(indent = '    ', newl = '')
        # HERE should using utf-8 encoding
        data = self.doc.toxml(encoding = 'utf-8')
        file = open(self.xml_file, "w")
        print >>file, data
        file.close()
    
    def printAniFull(self, ani):
        """this function can print out the ani's details"""
        # get the n in tag name, in int
        n = int( ani.getAttribute("n") )
        for child in ani.childNodes:
            if child.nodeType == Node.ELEMENT_NODE:
                if child.tagName == 'name':
                    print "No. %02i    %-50s" % (n, child.firstChild.nodeValue.encode('utf-8'))
                    down = child.getAttribute("down")
                    watch = child.getAttribute("watch")
                    url = child.getAttribute("url")
                    print "Down: %-10sWatch: %-10s" % (down, watch)
                    print "          %-50s" % url.encode('utf-8')
    
    def printAni(self, ani):
        """this function can print out the ani's details"""
        # get the n in tag name, in int
        n = int( ani.getAttribute("n") )
        for child in ani.childNodes:
            if child.nodeType == Node.ELEMENT_NODE:
                if child.tagName == 'name':
                    print "No. %02i    %-25s" % (n, child.firstChild.nodeValue.encode('utf-8')),
                    # when n is an even number, print out a new line
                    if n % 2 == 0:    print
    
    def searchAni(self, target):
        """this function can search the target element by numbers"""
        # get the list which contant all nodes that tagName is ani
        anis = self.doc.getElementsByTagName("ani")
        # find out the target element
        for ani in anis:
            # get the attribute n in all nodes ani
            # notics that n should be converted into int
            n = int( ani.getAttribute("n") )
            if n == target:
                # if it is the target element, jot it down
                found = ani
        # return target element
        return found
        
    def printAllAni(self, mode = 'simple'):
        """print out all entries inside the XML file"""
        if mode == 'simple':
            # in order to print out the data in xml 
            # only print the numbers and names
            for i in range(self.numbers):
                ani = self.searchAni(i+1)
                self.printAni(ani)
        elif mode == 'complex':
            # in this mode, this program will also print out the watch, down, url attribute
            for i in range(self.numbers):
                ani = self.searchAni(i+1)
                self.printAniFull(ani)          
                    
    def printXML(self, node, level = 0):
        """print out the XML file"""
        # get the type of the node
        data = node.__class__.__name__
        # if the node is an element node, also get the tag name
        if node.nodeType == Node.ELEMENT_NODE:
            data += "  tag: " + node.tagName
        # if the node is a text node, also get the text which
        # replace new line character to ⏎ and 
        # replace space character to _
        # notices that the string is in unicode form
        elif node.nodeType == Node.TEXT_NODE:
            data += ' :"%s"' % node.nodeValue.replace(u'\n', u'⏎').replace(u' ', u'_')
        # then print out the data, also insert the indent
        print " " * 4 * level, data.encode('utf-8')
        # if node has child nodes, go inside
        if node.hasChildNodes:
            for child in node.childNodes:
                # recall function itself
                self.printXML(child, level + 1)
                    
class main():
    """the main class, all functions are here"""
    def __init__(self):
        """class initial"""
        # set the command characters
        self.cmd_cha = ": "
        # set the Parser
        self.Parser = Parser()
        
        # set the continues value, using for exit program
        self.continues = 1
        while self.continues:
            # reset the Parser
            self.Parser.doc = minidom.parse(self.Parser.xml_file)
            # get the command from user input
            commands = raw_input(self.cmd_cha)
            command = commands.split()
            if commands == '':
                self.printHelp()
            elif command[0].isdigit():
                # if user is type a number, means call details function 
                # with an argument which is the number
                # Notics that details need an argument which is a list, inside the list is a number in string formate
                self.details( [ command[0] ] )
            else:
                try:
                    exec("self.%s(%s)" % (command[0], command[1:] ) )
                except (AttributeError, SyntaxError):
                    self.printHelp()
            
    def printHelp(self):
        """this finction using for print out an hep message"""
        print """available commands:
        all
        show
        details number
        watch
        down
        edit
        add name url info
        remove number
        quit
        """
    
    def all(self, data):
        """just print out all entries, the number and name"""
        self.Parser.printAllAni()
    
    def show(self, data):
        """print out all entries in XML file in full info"""
        self.Parser.printAllAni('complex')
    
    def details(self, data):
        """print out the details of an entrie"""
        # check the data
        ok = 1
        # make sure data only has one element and get the number inside data
        if ok and len(data) == 1:
            num = data[0]
        else:
            ok = 0
        # ensure data is digital and convert num into integer
        if ok and num.isdigit():
            num = int( num )
        else:
            ok = 0
        # ensure num is not over range
        if ok and num > 0 and num <= self.Parser.numbers:
            ani = self.Parser.searchAni(num)
            self.Parser.printAniFull(ani)
        else:
            print "Please input a correct number"
            print data
    
    def watch(self, data, att = 'watch'):
        """make the entrie's watch numbers plus one"""
        # check the data
        ok = 1
        # make sure data only has one element
        if ok and len(data) == 1:
            # if ok, get the number inside data
            num = data[0]
            act = '+1'
        elif len(data) == 2:
            num = data[0]
            act = data[1]
        else:
            ok = 0
        # ensure data is digital and convert num into integer
        if ok and num.isdigit():
            num = int( num )
        else:
            ok = 0
        # ensure act is available
        if ok and not act[0] == '+' and not act[1:].isdigit() and not act.isdigit():
            ok = 0
        # ensure num is not over range
        if ok and num > 0 and num <= self.Parser.numbers:
            self.Parser.editAttribute(num, att, act)
            # call details to print out the new details, 
            # here CAN just give the original data[0] as argument, but should put into a list
            self.details( [ data[0] ] )
        else:
            print "Please input a correct command"
    
    def down(self, data):
        """make the entrie's down numbers plus one"""
        # just use watch function, only change the attribute name
        self.watch(data, 'down')
    
    def edit(self, data):
        """edit an entrie in XML file"""
    
    def add(self, data):
        """add entrie in to XML file"""
    
    def remove(self, data):
        """remove the entrie in XML file"""
    
    def quit(self, data):
        """Exit Program"""
        print "Exit Program!!!"
        self.continues = 0
    
    def DeBug(self, data):
        """change to pdb mode"""
        pdb.set_trace()
        
        
if __name__ == '__main__':
    # Start program
    main()